import sys
import os
import logging
import subprocess
import shutil
import tempfile
import time
from typing import Optional
from PIL import Image
from fastmcp import FastMCP

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger("CrossPlatformPrinter")

# 初始化MCP服务
mcp = FastMCP("CrossPlatformPrinterService")

# 系统打印支持检查
printing_support = True
printer_module = None
if sys.platform.startswith('win32'):
    try:
        import win32print
        import win32ui
        from win32con import SW_HIDE
        printer_module = "win32print"
    except ImportError:
        printing_support = False
        logger.warning("Windows系统缺少pywin32库，打印功能受限")
elif sys.platform.startswith(('linux', 'darwin')):
    try:
        import cups
        printer_module = "cups"
    except ImportError:
        printing_support = False
        logger.warning(f"{sys.platform}系统缺少pycups库，打印功能受限")
else:
    printing_support = False
    logger.warning(f"不支持的操作系统: {sys.platform}")


@mcp.tool()
def get_system_info() -> dict:
    """获取当前系统信息和打印支持状态"""
    logger.info("获取系统打印信息")
    return {
        "os": sys.platform,
        "printing_supported": printing_support,
        "printer_module": printer_module,
        "supported_formats": ["txt", "docx", "xlsx", "pptx", "pdf", "md", "jpg", "jpeg", "gif", "png"]
    }


@mcp.tool()
def list_printers() -> list:
    """列出所有可用的打印机"""
    if not printing_support:
        error_msg = f"当前系统({sys.platform})不支持打印功能或未安装必要库"
        logger.error(error_msg)
        return {"error": error_msg}

    printers = []
    logger.info("获取打印机列表")

    try:
        if sys.platform.startswith('win32'):
            printers = [printer[2] for printer in
                        win32print.EnumPrinters(win32print.PRINTER_ENUM_LOCAL | win32print.PRINTER_ENUM_CONNECTIONS)]

        elif sys.platform.startswith(('linux', 'darwin')):
            conn = cups.Connection()
            printers = list(conn.getPrinters().keys())

        logger.info(f"成功获取{len(printers)}台打印机")
    except Exception as e:
        error_msg = f"获取打印机列表失败: {str(e)}"
        logger.error(error_msg)
        return {"error": error_msg}

    return printers


@mcp.tool()
def get_default_printer() -> str:
    """获取默认打印机名称"""
    if not printing_support:
        error_msg = f"当前系统({sys.platform})不支持打印功能或未安装必要库"
        logger.error(error_msg)
        return {"error": error_msg}

    try:
        if sys.platform.startswith('win32'):
            default_printer = win32print.GetDefaultPrinter()
        elif sys.platform.startswith(('linux', 'darwin')):
            conn = cups.Connection()
            default_printer = conn.getDefault()

        logger.info(f"默认打印机: {default_printer}")
        return default_printer

    except Exception as e:
        error_msg = f"获取默认打印机失败: {str(e)}"
        logger.error(error_msg)
        return {"error": error_msg}


# 原始打印函数（核心逻辑，未被装饰）
def _raw_print_file(
        file_path: str,
        printer_name: Optional[str] = None,
        copies: int = 1,
        duplex: bool = False,
        color_mode: str = "color",
        paper_size: str = "A4"
) -> dict:
    """原始打印逻辑（未被@mcp.tool()装饰，可直接调用）"""
    logger.info(f"=== 打印任务开始 (系统: {sys.platform}) ===")
    logger.info(f"原始文件路径: {file_path} (绝对路径: {os.path.abspath(file_path)})")
    logger.info(f"参数: 打印机={printer_name}, 份数={copies}, 双面={duplex}, 颜色={color_mode}, 纸张={paper_size}")

    if not printing_support:
        error_msg = f"当前系统({sys.platform})不支持打印功能或未安装必要库"
        logger.error(error_msg)
        return {"error": error_msg}

    # 检查文件是否存在及权限
    if not os.path.exists(file_path):
        error_msg = f"文件不存在: {file_path}"
        logger.error(error_msg)
        return {"error": error_msg}
    if not os.access(file_path, os.R_OK):
        error_msg = f"无权限读取文件: {file_path} (当前用户: {os.getlogin() if sys.platform != 'linux' else os.getuid()})"
        logger.error(error_msg)
        return {"error": error_msg}

    # 检查文件格式
    file_ext = os.path.splitext(file_path)[1].lower()[1:]
    supported_formats = ["txt", "docx", "xlsx", "pptx", "pdf", "md", "jpg", "jpeg", "gif", "png"]
    if file_ext not in supported_formats:
        error_msg = f"不支持的文件格式: {file_ext}，支持的格式: {', '.join(supported_formats)}"
        logger.error(error_msg)
        return {"error": error_msg}

    # 需要转换为PDF的格式列表
    convert_formats = ["docx", "xlsx", "pptx", "md", "txt"]
    temp_dir = None
    temp_pdf_path = None
    original_file_path = file_path
    converted = False

    try:
        # 1. 格式转换：根据不同系统优化
        if file_ext in convert_formats:
            # 查找合适的转换工具
            office_cmd = _find_office_application()
            if not office_cmd:
                error_msg = f"{sys.platform}系统未检测到可用的办公软件，请安装LibreOffice或WPS"
                logger.error(error_msg)
                return {"error": error_msg}

            # 创建系统兼容的临时目录
            temp_dir = _create_compatible_temp_dir()
            temp_dir_path = temp_dir.name
            logger.info(f"临时转换目录: {temp_dir_path}")

            # 执行转换
            conversion_success, output = _convert_to_pdf(office_cmd, file_path, temp_dir_path)
            if not conversion_success:
                error_msg = f"文件转换失败: {output[:500]}"
                logger.error(error_msg)
                return {"error": error_msg}

            # 查找转换后的PDF文件
            temp_pdf_path = _find_converted_pdf(temp_dir_path, original_file_path)
            if not temp_pdf_path:
                error_msg = "转换成功但未找到生成的PDF文件"
                logger.error(error_msg)
                return {"error": error_msg}

            # 验证PDF有效性
            if not _is_valid_pdf(temp_pdf_path):
                error_msg = "生成的文件不是有效的PDF格式"
                logger.error(error_msg)
                return {"error": error_msg}

            # 更新为转换后的PDF路径
            file_path = temp_pdf_path
            file_ext = "pdf"
            converted = True
            logger.info(f"文件转换成功: {file_path}")

        # 2. 确定打印机
        target_printer = printer_name or get_default_printer()
        if isinstance(target_printer, dict) and "error" in target_printer:
            return target_printer

        # 3. 执行打印（按系统处理）
        if sys.platform.startswith('win32'):
            result = _print_windows(file_path, file_ext, target_printer, copies, duplex, color_mode)
        elif sys.platform.startswith('linux'):
            result = _print_linux(file_path, file_ext, target_printer, copies, duplex, color_mode, paper_size)
        elif sys.platform.startswith('darwin'):
            result = _print_macos(file_path, file_ext, target_printer, copies, duplex, color_mode, paper_size)
        else:
            error_msg = f"不支持的操作系统: {sys.platform}"
            logger.error(error_msg)
            return {"error": error_msg}

        # 整理结果
        result["original_file"] = original_file_path
        result["converted_to_pdf"] = converted
        logger.info(f"=== 打印任务成功完成 ===")
        return result

    except Exception as e:
        error_msg = f"打印失败: {str(e)}"
        logger.error(f"=== 打印任务失败 ===", exc_info=True)
        return {"error": error_msg}

    finally:
        # 清理临时文件（调试时可注释）
        if temp_dir:
            try:
                temp_dir.cleanup()
                logger.info(f"临时目录已清理: {temp_dir.name}")
            except Exception as e:
                logger.warning(f"清理临时目录失败: {str(e)}，请手动删除: {temp_dir.name}")


@mcp.tool()
def print_file(
        file_path: str,
        printer_name: Optional[str] = None,
        copies: int = 1,
        duplex: bool = False,
        color_mode: str = "color",
        paper_size: str = "A4"
) -> dict:
    """
    跨平台打印指定文件，自动处理格式转换

    Args:
        file_path: 要打印的文件路径
        printer_name: 打印机名称，不指定则使用默认打印机
        copies: 打印份数
        duplex: 是否双面打印
        color_mode: 颜色模式，可选"color"（彩色）或"monochrome"（黑白）
        paper_size: 纸张大小，如"A4"、"Letter"等

    Returns:
        打印结果信息
    """
    return _raw_print_file(
        file_path=file_path,
        printer_name=printer_name,
        copies=copies,
        duplex=duplex,
        color_mode=color_mode,
        paper_size=paper_size
    )


@mcp.tool()
def print_text(
        text: str,
        printer_name: Optional[str] = None,
        copies: int = 1,
        duplex: bool = False,
        color_mode: str = "color",
        paper_size: str = "A4"
) -> dict:
    """
    打印文本内容

    Args:
        text: 要打印的文本内容
        printer_name: 打印机名称，不指定则使用默认打印机
        copies: 打印份数
        duplex: 是否双面打印
        color_mode: 颜色模式，可选"color"（彩色）或"monochrome"（黑白）
        paper_size: 纸张大小，如"A4"、"Letter"等

    Returns:
        打印结果信息
    """
    logger.info(f"开始打印文本，长度: {len(text)}字符")

    if not printing_support:
        error_msg = f"当前系统({sys.platform})不支持打印功能或未安装必要库"
        logger.error(error_msg)
        return {"error": error_msg}

    # 创建临时文本文件
    with tempfile.NamedTemporaryFile(mode='w', suffix=".txt", delete=False) as temp_file:
        temp_file.write(text)
        temp_file_path = temp_file.name

    try:
        # 调用原始打印函数
        return _raw_print_file(
            file_path=temp_file_path,
            printer_name=printer_name,
            copies=copies,
            duplex=duplex,
            color_mode=color_mode,
            paper_size=paper_size
        )
    finally:
        # 确保临时文件被删除
        if os.path.exists(temp_file_path):
            try:
                os.unlink(temp_file_path)
                logger.info(f"已清理临时文本文件: {temp_file_path}")
            except Exception as e:
                logger.warning(f"清理临时文本文件失败: {str(e)}")


# 平台特定辅助函数
def _find_office_application():
    """根据系统查找合适的办公软件（LibreOffice/WPS）"""
    if sys.platform.startswith('win32'):
        possible_paths = [
            "C:\\Program Files\\LibreOffice\\program\\soffice.exe",
            "C:\\Program Files (x86)\\LibreOffice\\program\\soffice.exe",
            "C:\\Program Files\\Kingsoft\\WPS Office\\11.1.0.11719\\office6\\wps.exe",
            "C:\\Program Files (x86)\\Kingsoft\\WPS Office\\11.1.0.11719\\office6\\wps.exe"
        ]
        for path in possible_paths:
            if os.path.exists(path):
                logger.info(f"Windows检测到办公软件: {path}")
                return path
        return None

    elif sys.platform.startswith('linux'):
        linux_office_cmds = [
            "libreoffice", "soffice",
            "/usr/bin/libreoffice", "/usr/bin/soffice",
            "/usr/local/bin/libreoffice", "/usr/local/bin/soffice"
        ]
        for cmd in linux_office_cmds:
            try:
                subprocess.run([cmd, "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
                logger.info(f"Linux检测到办公软件: {cmd}")
                return cmd
            except (FileNotFoundError, subprocess.CalledProcessError):
                continue
        return None

    elif sys.platform.startswith('darwin'):
        possible_paths = [
            "/Applications/LibreOffice.app/Contents/MacOS/soffice",
            "/Applications/WPS Office.app/Contents/MacOS/WPS"
        ]
        for path in possible_paths:
            if os.path.exists(path):
                logger.info(f"macOS检测到办公软件: {path}")
                return path
        return None

    return None


def _create_compatible_temp_dir():
    """创建系统兼容的临时目录"""
    if sys.platform.startswith('linux'):
        user_temp_dir = os.path.join(os.path.expanduser("~"), ".print_temp")
        os.makedirs(user_temp_dir, exist_ok=True)
        os.chmod(user_temp_dir, 0o755)
        return tempfile.TemporaryDirectory(prefix="linux_print_", suffix="_pdf", dir=user_temp_dir)
    elif sys.platform.startswith('win32'):
        return tempfile.TemporaryDirectory(prefix="win_print_", suffix="_pdf")
    elif sys.platform.startswith('darwin'):
        user_temp_dir = os.path.join(os.path.expanduser("~"), "Library/Caches/PrintTemp")
        os.makedirs(user_temp_dir, exist_ok=True)
        return tempfile.TemporaryDirectory(prefix="mac_print_", suffix="_pdf", dir=user_temp_dir)
    return tempfile.TemporaryDirectory(prefix="print_", suffix="_pdf")


def _convert_to_pdf(office_cmd, input_path, output_dir):
    """根据系统特性执行PDF转换"""
    base_args = [
        office_cmd,
        "--headless",
        "--norestore",
        "--nofirststartwizard",
        "--convert-to", "pdf",
        "--outdir", output_dir,
        os.path.abspath(input_path)
    ]

    env = os.environ.copy()
    if sys.platform.startswith('linux'):
        env["LC_ALL"] = "zh_CN.UTF-8"
        env["LANG"] = "zh_CN.UTF-8"
        timeout = 300
    elif sys.platform.startswith('win32'):
        timeout = 180
    elif sys.platform.startswith('darwin'):
        env["LC_ALL"] = "zh_CN.UTF-8"
        timeout = 240

    try:
        logger.info(f"执行转换命令: {' '.join(base_args)}")
        result = subprocess.run(
            base_args,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            text=True,
            timeout=timeout,
            env=env
        )

        logger.info(f"=== 转换输出 ===\n{result.stdout}\n=== 转换结束 ===")
        return (result.returncode == 0, result.stdout)
    except subprocess.TimeoutExpired:
        return (False, f"转换超时（超过{timeout}秒）")
    except Exception as e:
        return (False, f"转换过程出错: {str(e)}")


def _find_converted_pdf(temp_dir, original_path):
    """根据系统特性查找转换后的PDF文件"""
    original_name = os.path.splitext(os.path.basename(original_path))[0]
    logger.info(f"查找PDF文件，原始文件名: {original_name}")

    time.sleep(2 if sys.platform.startswith('linux') else 1)

    try:
        if sys.platform.startswith('linux'):
            temp_files_bytes = os.listdir(temp_dir.encode('utf-8'))
            temp_files = [f.decode('utf-8', errors='replace') for f in temp_files_bytes]
        else:
            temp_files = os.listdir(temp_dir)

        logger.info(f"临时目录文件列表 ({len(temp_files)}个): {temp_files}")
    except Exception as e:
        logger.error(f"列出临时目录文件失败: {str(e)}")
        return None

    pdf_files = []
    for f in temp_files:
        if f.lower().endswith('.pdf'):
            if original_name in f:
                pdf_files.append((os.path.join(temp_dir, f), 2))
            else:
                pdf_files.append((os.path.join(temp_dir, f), 1))

    if not pdf_files:
        return None

    pdf_files.sort(key=lambda x: (x[1], os.path.getsize(x[0])), reverse=True)
    return pdf_files[0][0]


def _is_valid_pdf(file_path):
    """验证PDF文件有效性"""
    try:
        if os.path.getsize(file_path) < 100:
            return False

        with open(file_path, 'rb') as f:
            header = f.read(4)
            return header == b'%PDF'
    except Exception as e:
        logger.error(f"验证PDF文件失败: {str(e)}")
        return False


def _print_windows(file_path, file_ext, printer, copies, duplex, color_mode):
    """Windows系统打印实现"""
    try:
        import win32api
        import win32con

        if file_ext in ["jpg", "jpeg", "gif", "png"]:
            img = Image.open(file_path)
            with tempfile.NamedTemporaryFile(suffix=".bmp", delete=False) as temp_file:
                img.save(temp_file, "BMP")
                temp_file_path = temp_file.name

            hprinter = win32print.OpenPrinter(printer)
            try:
                devmode = win32print.GetPrinter(hprinter, 2)[1]
                devmode.Fields |= win32print.DM_DUPLEX if duplex else 0
                devmode.Duplex = win32print.DM_DUPLEX_HORIZONTAL if duplex else win32print.DM_DUPLEX_SIMPLEX
                devmode.Color = 2 if color_mode == "color" else 1

                for _ in range(copies):
                    win32print.StartDocPrinter(hprinter, 1, ("Image Print", None, "RAW"))
                    win32print.StartPagePrinter(hprinter)
                    with open(temp_file_path, "rb") as f:
                        win32print.WritePrinter(hprinter, f.read())
                    win32print.EndPagePrinter(hprinter)
                    win32print.EndDocPrinter(hprinter)

            finally:
                win32print.ClosePrinter(hprinter)
                os.unlink(temp_file_path)

        else:
            for _ in range(copies):
                print_args = f'/d:"{printer}"'
                if duplex:
                    print_args += ' /h'
                win32api.ShellExecute(
                    0, "print", file_path, print_args, ".", win32con.SW_HIDE
                )

        return {
            "success": True,
            "message": f"文件已发送到Windows打印机",
            "printer": printer,
            "copies": copies,
            "duplex": duplex,
            "color_mode": color_mode
        }
    except Exception as e:
        raise RuntimeError(f"Windows打印失败: {str(e)}")


def _print_linux(file_path, file_ext, printer, copies, duplex, color_mode, paper_size):
    """Linux系统打印实现"""
    try:
        conn = cups.Connection()

        if isinstance(printer, str):
            try:
                printer = printer.encode('utf-8').decode('utf-8')
            except UnicodeEncodeError:
                logger.warning(f"打印机名称包含无法编码的字符: {printer}")

        options = {
            "media": paper_size,
            "copies": str(copies),
            "job-sheets": "none,none"
        }
        if duplex:
            options["duplex"] = "sides=two-sided-long-edge"
        if color_mode == "monochrome":
            options["print-color-mode"] = "monochrome"
        if file_ext in ["jpg", "jpeg", "gif", "png"]:
            options["fit-to-page"] = "True"

        try:
            file_path = file_path.encode('utf-8').decode('utf-8')
        except UnicodeEncodeError:
            raise RuntimeError(f"文件路径包含无法编码的字符: {file_path}")

        job_id = conn.printFile(
            printer,
            file_path,
            f"Print Job: {os.path.basename(file_path)}",
            options
        )
        logger.info(f"Linux CUPS打印任务ID: {job_id}")

        return {
            "success": True,
            "message": f"文件已发送到Linux打印机",
            "printer": printer,
            "job_id": job_id,
            "copies": copies,
            "duplex": duplex,
            "color_mode": color_mode,
            "paper_size": paper_size
        }
    except Exception as e:
        raise RuntimeError(f"Linux打印失败: {str(e)}")


def _print_macos(file_path, file_ext, printer, copies, duplex, color_mode, paper_size):
    """macOS系统打印实现"""
    try:
        cmd = ['lpr']

        if printer:
            cmd.extend(['-P', printer])
        if copies > 1:
            cmd.extend(['-#', str(copies)])
        if duplex:
            cmd.append('-o duplex=yes')
        if color_mode == "monochrome":
            cmd.append('-o ColorModel=Gray')
        if paper_size:
            cmd.append(f'-o media={paper_size}')
        if file_ext in ["jpg", "jpeg", "gif", "png"]:
            cmd.append('-o fit-to-page')

        cmd.append(file_path)

        logger.info(f"执行macOS打印命令: {' '.join(cmd)}")
        result = subprocess.run(
            cmd,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
            check=True
        )

        return {
            "success": True,
            "message": f"文件已发送到macOS打印机",
            "printer": printer or "默认打印机",
            "copies": copies,
            "duplex": duplex,
            "color_mode": color_mode,
            "paper_size": paper_size
        }
    except subprocess.CalledProcessError as e:
        raise RuntimeError(f"macOS打印失败: {e.stderr}")
    except Exception as e:
        raise RuntimeError(f"macOS打印失败: {str(e)}")


if __name__ == "__main__":
    if "--http" in sys.argv:
        logger.info("启动HTTP服务（SSE 模式）")
        mcp.run(host="0.0.0.0", port=9118, transport="sse")
    else:
        logger.info("启动Stdio服务")
        mcp.run(transport="stdio")
